#include "common.h"
#include "services.h"
#include "popwin.h"
#include "message_pump.h"
#include "../res/resource.h"
#include "popup_history.h"
#include <time.h>
#include "str_utils.h"

#define NUM_SERVICES		13
HANDLE hService[NUM_SERVICES];
HANDLE hMenuShowHistory, hMenuToggleOnOff;

void StripBBCodesInPlace(wchar_t *text) {
	if(!DBGetContactSettingByte(0, MODULE, "StripBBCodes", 1))
		return;

	int read = 0, write = 0;
	int len = wcslen(text);

	while(read <= len) { // copy terminating null too
		while(read <= len && text[read] != L'[') {
			if(text[read] != text[write]) text[write] = text[read];
			read++; write++;
		}
		if(read > len) break;

		if(len - read >= 3 && (wcsnicmp(text + read, L"[b]", 3) == 0 || wcsnicmp(text + read, L"[i]", 3) == 0))
			read += 3;
		else if(len - read >= 4 && (wcsnicmp(text + read, L"[/b]", 4) == 0 || wcsnicmp(text + read, L"[/i]", 4) == 0))
			read += 4;
		else if(len - read >= 6 && (wcsnicmp(text + read, L"[color", 6) == 0)) {
			while(read < len && text[read] != L']') read++; 
			read++;// skip the ']'
		} else if(len - read >= 8 && (wcsnicmp(text + read, L"[/color]", 8) == 0))
			read += 8;
		else {
			if(text[read] != text[write]) text[write] = text[read];
			read++; write++;
		}
	}
}

int CreatePopupA(WPARAM wParam, LPARAM lParam) {
	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) return 0;

	POPUPDATA *pd_in = (POPUPDATA *)wParam;
	POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW));

	MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME);
	MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE);
	pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0;
	pd_out->lpwzText[MAX_SECONDLINE - 1] = 0;
	StripBBCodesInPlace(pd_out->lpwzContactName);
	StripBBCodesInPlace(pd_out->lpwzText);

	pd_out->lchContact = pd_in->lchContact;
	pd_out->lchIcon = pd_in->lchIcon;
	if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg'
		pd_out->colorBack = pd_out->colorText = 0;
	else {
		pd_out->colorBack = pd_in->colorBack & 0xFFFFFF;
		pd_out->colorText = pd_in->colorText & 0xFFFFFF;
	}
	pd_out->PluginWindowProc = pd_in->PluginWindowProc;
	pd_out->PluginData = pd_in->PluginData;
	pd_out->iSeconds = 0;

	lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0));

	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) {
		free(pd_out);
		return 0;
	}

	//MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupA"), MB_OK);
	PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out);
	return 0;
}

int CreatePopupExA(WPARAM wParam, LPARAM lParam) {

	POPUPDATAEX *pd_in = (POPUPDATAEX *)wParam;
	POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW));

	MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME);
	MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE);
	pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0;
	pd_out->lpwzText[MAX_SECONDLINE - 1] = 0;
	StripBBCodesInPlace(pd_out->lpwzContactName);
	StripBBCodesInPlace(pd_out->lpwzText);

	pd_out->lchContact = pd_in->lchContact;
	pd_out->lchIcon = pd_in->lchIcon;
	if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg'
		pd_out->colorBack = pd_out->colorText = 0;
	else {
		pd_out->colorBack = pd_in->colorBack & 0xFFFFFF;
		pd_out->colorText = pd_in->colorText & 0xFFFFFF;
	}
	pd_out->PluginWindowProc = pd_in->PluginWindowProc;
	pd_out->PluginData = pd_in->PluginData;
	pd_out->iSeconds = pd_in->iSeconds;

	lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0));
	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) {
		free(pd_out);
		return 0;
	}

	//MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupExA"), MB_OK);
	PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out);
	return 0;
}

int CreatePopupW(WPARAM wParam, LPARAM lParam) {

	POPUPDATAW *pd_in = (POPUPDATAW *)wParam;
	POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW));
	memcpy(pd_out, pd_in, sizeof(POPUPDATAW));
	pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0;
	pd_out->lpwzText[MAX_SECONDLINE - 1] = 0;
	StripBBCodesInPlace(pd_out->lpwzContactName);
	StripBBCodesInPlace(pd_out->lpwzText);

	if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg'
		pd_out->colorBack = pd_out->colorText = 0;
	else {
		pd_out->colorBack &= 0xFFFFFF;
		pd_out->colorText &= 0xFFFFFF;
	}

	lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0));
	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) {
		free(pd_out);
		return 0;
	}

	//MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupW"), MB_OK);
	PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out);
	return 0;
}

int ChangeTextW(WPARAM wParam, LPARAM lParam) {
	HWND hwndPop = (HWND)wParam;
	wchar_t *newText = wcsdup((wchar_t *)lParam);
	StripBBCodesInPlace(newText);

	if(IsWindow(hwndPop))
		PostMessage(hwndPop, PUM_SETTEXT, 0, (LPARAM)newText);
	return 0;
}

int ChangeTextA(WPARAM wParam, LPARAM lParam) {
	HWND hwndPop = (HWND)wParam;
	char *newText = (char *)lParam;
	wchar_t buff[MAX_SECONDLINE];
	MultiByteToWideChar(code_page, 0, newText, -1, buff, MAX_SECONDLINE);
	buff[MAX_SECONDLINE - 1] = 0;
	StripBBCodesInPlace(buff);

	if(IsWindow(hwndPop))
		PostMessage(hwndPop, PUM_SETTEXT, 0, (LPARAM)wcsdup(buff));
	return 0;
}

int GetContact(WPARAM wParam, LPARAM lParam) {
	HWND hwndPop = (HWND)wParam;
	HANDLE hContact;
	if(GetCurrentThreadId() == message_pump_thread_id) {
		SendMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, 0);
	} else {
		HANDLE hEvent = CreateEvent(0, 0, 0, 0);
		PostMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, (LPARAM)hEvent);
		//WaitForSingleObject(hEvent, INFINITE);
		MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0);
		CloseHandle(hEvent);
	}

	return (int)hContact;
}

int GetPluginData(WPARAM wParam, LPARAM lParam) {
	HWND hwndPop = (HWND)wParam;
	void *data = 0;
	if(GetCurrentThreadId() == message_pump_thread_id) {
		SendMessage(hwndPop, PUM_GETPLUGINDATA, (WPARAM)&data, 0);
	} else {
		HANDLE hEvent = CreateEvent(0, 0, 0, 0);
		PostMessage(hwndPop, PUM_GETPLUGINDATA, (WPARAM)&data, (LPARAM)hEvent);
		//WaitForSingleObject(hEvent, INFINITE);
		MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0);
		CloseHandle(hEvent);
	}

	if(lParam && !ignore_gpd_passed_addy) {
		void **pData = (void **)lParam;
		*pData = data;
	}

	return (int)data;
}

int IsSecondLineShown(WPARAM wParam, LPARAM lParam) {
	return TRUE;
}

int PopupQuery(WPARAM wParam, LPARAM lParam) {
	switch(wParam) {
		case PUQS_ENABLEPOPUPS:
			{
				bool enabled = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1);
				if(!enabled) DBWriteContactSettingByte(0, MODULE, "Enabled", 1);
				return enabled ? 0: 1;
			}
			break;
		case PUQS_DISABLEPOPUPS:
			{
				bool enabled = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1);
				if(enabled) DBWriteContactSettingByte(0, MODULE, "Enabled", 0);
				DBWriteContactSettingByte(0, MODULE, "Enabled", 0);
				return enabled ? 1 : 0;
			}
			break;
		case PUQS_GETSTATUS:
			return DBGetContactSettingByte(0, MODULE, "Enabled", 1);
		default:
			return 1;
	}
	return 0;
}

int TogglePopups(WPARAM wParam, LPARAM lParam) {
	CLISTMENUITEM menu;
	menu.cbSize = sizeof(CLISTMENUITEM);
	// just write to db - the rest is handled in the Meta_SettingChanged function
	if(DBGetContactSettingByte(0, MODULE, "Enabled", 1)) {
		DBWriteContactSettingByte(0, MODULE, "Enabled", 0);
		// modify main menu item
		menu.flags = CMIM_NAME;// | CMIM_ICON;
		menu.hIcon=0; //LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MCMENUOFF));
		menu.pszName = (char *)Translate("Enable Popups");
		CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuToggleOnOff, (LPARAM)&menu);
	} else {
		DBWriteContactSettingByte(0, MODULE, "Enabled", 1);
		// modify main menu item
		menu.flags = CMIM_NAME;// | CMIM_ICON;
		menu.hIcon=0;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MCMENU));
		menu.pszName = (char *)Translate("Disable Popups");
		CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuToggleOnOff, (LPARAM)&menu);
	}
	return 0;
}

int PopupChange(WPARAM wParam, LPARAM lParam) {
	HWND hwndPop = (HWND)wParam;
	POPUPDATAEX *pd_in = (POPUPDATAEX *)lParam;

	if(IsWindow(hwndPop)) {
		POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW));

		MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME);
		MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE);

		pd_out->lchContact = pd_in->lchContact;
		pd_out->lchIcon = pd_in->lchIcon;
		if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg'
			pd_out->colorBack = pd_out->colorText = 0;
		else {
			pd_out->colorBack = pd_in->colorBack & 0xFFFFFF;
			pd_out->colorText = pd_in->colorText & 0xFFFFFF;
		}
		pd_out->colorBack = pd_in->colorBack;
		pd_out->colorText = pd_in->colorText;
		pd_out->PluginWindowProc = pd_in->PluginWindowProc;
		pd_out->PluginData = pd_in->PluginData;
		pd_out->iSeconds = pd_in->iSeconds;

		lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0));
	
		PostMessage(hwndPop, PUM_CHANGE, 0, (LPARAM)pd_out);
	}
	return 0;
}

int ShowMessage(WPARAM wParam, LPARAM lParam) {
	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) return 0;

	POPUPDATAT pd = {0};
	_tcscpy(pd.lptzContactName, lParam == SM_WARNING ? _T("Warning") : _T("Notification"));
	pd.lchIcon = LoadIcon(0, lParam == SM_WARNING ? IDI_WARNING : IDI_INFORMATION);
	TCHAR buff[MAX_SECONDLINE];
	a2t((char *)wParam, buff, MAX_SECONDLINE);
	_tcscpy(pd.lptzText, buff);

	CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&pd, 0);

	return 0;
}

int ShowMessageW(WPARAM wParam, LPARAM lParam) {
	if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) return 0;

	POPUPDATAW pd = {0};
	wcscpy(pd.lpwzContactName, lParam == SM_WARNING ? L"Warning" : L"Notification");
	pd.lchIcon = LoadIcon(0, lParam == SM_WARNING ? IDI_WARNING : IDI_INFORMATION);
	wcsncpy(pd.lpwzText, (wchar_t *)wParam, MAX_SECONDLINE);

	CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0);

	return 0;
}

//=====PopUp/ShowHistory
//extern BOOL CALLBACK DlgProcHistLstOpts(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int PopUp_ShowHistory(WPARAM wParam, LPARAM lParam)
{
	if (!hHistoryWindow) {
		hHistoryWindow = CreateDialog(hInst, MAKEINTRESOURCE(IDD_LST_HISTORY), NULL, DlgProcHistLst);
	}
	ShowWindow(hHistoryWindow, SW_SHOW);
	return 0;
}

int PrebuildMenu(WPARAM wParam, LPARAM lParam) {
	return 0;
}

HANDLE hEventBuildMenu;

void InitServices() {
	int i = 0;
	hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUP, CreatePopupA);
	hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUPEX, CreatePopupExA);
	hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUPW, CreatePopupW);
	hService[i++] = CreateServiceFunction(MS_POPUP_CHANGETEXTW, ChangeTextW);
	hService[i++] = CreateServiceFunction(MS_POPUP_CHANGETEXT, ChangeTextA);
	hService[i++] = CreateServiceFunction(MS_POPUP_CHANGE, PopupChange);
	hService[i++] = CreateServiceFunction(MS_POPUP_GETCONTACT, GetContact);
	hService[i++] = CreateServiceFunction(MS_POPUP_GETPLUGINDATA, GetPluginData);
	hService[i++] = CreateServiceFunction(MS_POPUP_ISSECONDLINESHOWN, IsSecondLineShown);
	hService[i++] = CreateServiceFunction(MS_POPUP_QUERY, PopupQuery);

	hService[i++] = CreateServiceFunction(MS_POPUP_SHOWMESSAGE, ShowMessage);
	hService[i++] = CreateServiceFunction(MS_POPUP_SHOWMESSAGE"W", ShowMessageW);

	hService[i++] = CreateServiceFunction(MS_POPUP_SHOWHISTORY, PopUp_ShowHistory);
	hService[i++] = CreateServiceFunction("PopUp/ToggleEnabled", TogglePopups);

	CLISTMENUITEM menu = {0};

	menu.cbSize=sizeof(menu);
	menu.flags = CMIM_ALL;

	menu.hIcon=0;
	menu.position = 500010000;
	menu.pszPopupName = Translate("PopUps");

	hiPopupHistory = LoadIcon(hInst, MAKEINTRESOURCE(IDI_POPUP_HISTORY));
	menu.hIcon = hiPopupHistory;
	menu.pszService= MS_POPUP_SHOWHISTORY;
	menu.pszName = Translate("Popup History");
	hMenuShowHistory = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&menu);
	menu.hIcon = 0;

	menu.pszService= "PopUp/ToggleEnabled";
	menu.pszName = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1 ? Translate("Disable Popups") : Translate("Enable Popups"));
	hMenuToggleOnOff = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&menu);

	hEventBuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildMenu);

	if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) 
		code_page = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0);
}

void DeinitServices() {
	UnhookEvent(hEventBuildMenu);

	for(int i = 0; i < NUM_SERVICES; i++)
		if(hService[i]) DestroyServiceFunction(hService[i]);
}
